<?php

namespace App\Kuafu\Shop\Service;

use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use App\Kuafu\Shop\Model\Shop;
use Illuminate\Support\Facades\Redis;

class ShopCodeService extends BaseService
{
    /**
     * 当前最大的shopCode
     */
    private const SHOP_CODE_MAX  = 'SHOP_CODE_MAX';

    /**
     * 初始化时原子锁
     */
    private const SHOP_CODE_LOCK = 'SHOP_CODE_LOCK';

    /**
     * 生成新的shopCode
     * @return int
     */
    public function createShopCode(): int
    {
        $this->initRedisValue();

        do {
            $cShopCode = $this->incrShopCode();
        } while (!$this->checkShopCode($cShopCode));

        return $cShopCode;
    }

    /**
     * 当redis异常时（过分高），可手动执行
     * @return mixed
     */
    public function clearRedis()
    {
        return Redis::del(self::SHOP_CODE_MAX);
    }

    /*
     * 初始化redis中值，确保环境正常
     */
    private function initRedisValue(): void
    {
        if (Redis::exists(self::SHOP_CODE_MAX)) {
            return;
        }

        // 从DB生成Redis，锁住
        $lockKey = self::SHOP_CODE_LOCK;
        if (!Redis::setnx($lockKey, time())) {
            // 防止死锁
            $lock_time = Redis::get($lockKey);
            if (($lock_time + 5) < time()) {
                Redis::del($lockKey);
            } else {
                throw new ShopException(ShopCodeEnum::ERR_SHOP_LOCK);
            }
        }

        /**
         * 定个初始值，上线前检查下，双库中是否都含shop_code_init
         * orm-prod
         * select * from store where brand_id = 1 and shop_code < 100000 order by shop_code desc
         */
        $initCode = config('shop.shop_code_init');
        $checkCode = Shop::query()
                         ->where('shopCode', $initCode)
                         ->selectRaw(1)
                         ->count();
        if ($checkCode) {
            $occurShopCode = (int)Shop::query()
                                      ->where('shopCode', '>', $initCode)
                                      ->where('shopCode', '<', $initCode + 1000)
                                      ->orderBy('shopCode')
                                      ->value('shopCode');
        } else {
            $occurShopCode = $initCode;
        }

        Redis::set(self::SHOP_CODE_MAX, $occurShopCode);
    }

    /**
     * shopCode按需自增
     * @return int
     */
    private function incrShopCode(): int
    {
        return Redis::incr(self::SHOP_CODE_MAX);
    }

    /**
     * 获取当前自增器的shopCode
     * @return int
     */
    public function getShopCode(): int
    {
        return Redis::get(self::SHOP_CODE_MAX);
    }

    /**
     * 检查shopCode合规性
     * @param int $shopCode
     * @return bool
     */
    private function checkShopCode(int $shopCode): bool
    {
        // 包含4，暂时不要了
//        if (strpos((string)$shopCode, "4") !== false) {
//            return false;
//        }

        // 数据库是否冲突
        if (Shop::withoutGlobalScope('isDel')
                ->where('shopCode', $shopCode)
                ->selectRaw("1")
                ->count()) {
            return false;
        }

        return true;
    }
}
